import recordCodeMap, { EOperationType } from '../../config/recordMap.config';
import { UserInfoEntity } from '../model/user';
import { Context } from 'egg';
import { IAopController } from '../controller/user';

interface ICommonAopProps extends IAopController {
  ctx: Context;
  code: string;
}

interface ISuperAopPros extends ICommonAopProps {
  message: string;
}

class AopManager {
  private _aopObj = {};

  public getAop(code: string) {
    return this._aopObj[code] || null;
  }

  public addAop(code: string, clazz) {
    if (this._aopObj[code]) {
      console.log('重复注册apo');
      return;
    }
    this._aopObj[code] = clazz;
  }

  public removeAop(code: string) {
    if (this._aopObj[code]) {
      this._aopObj[code] = null;
    }
  }

  public record(params: ICommonAopProps) {
    const { code, ctx, subject, object } = params;
    let recorder: CommonAop;
    const recordConstruct = this.getAop(code);
    const verbFunc = recordCodeMap[code].message;
    const message = verbFunc(subject, object);
    if (recordConstruct) {
      recorder = new recordConstruct({ message, ...params });
    } else {
      recorder = new CommonAop(ctx, message);
    }
    recorder.record();
  }
}

class CommonAop {
  message = '';
  detail = '';
  ctx: Context;
  constructor(ctx: Context, message?: string, detail?: string) {
    this.message = message || this.message;
    this.detail = detail || this.detail;
    this.ctx = ctx;
  }
  async record(message?: string, detail?: string) {
    console.log('message', message || this.message);
    console.log('detail', detail || this.detail);
    await this.ctx.model.OperationLog.create({
      message: message || this.message,
      detail: detail || this.detail,
    });
  }
}

class SuperAop extends CommonAop {
  /** controller执行后时传入的数据 */
  requestData;
  /** controller执行后的返回的结果数据 */
  resultData;
  /** controller执行时的上下文 */
  ctx: Context;
  /** 记录代号 */
  code: string;
  /** 记录消息 */
  message: string;
  /** 当前用户信息 */
  userInfo: UserInfoEntity;
  constructor(props: ISuperAopPros) {
    const { ctx,
            code,
            message,
            requestData,
            resultData } = props;
    super(ctx);
    this.requestData = requestData;
    this.resultData = resultData;
    this.ctx = ctx;
    this.code = code;
    this.message = message;
    this.userInfo = ctx.state.user && ctx.state.user.userInfo;
  }
  async record(message?: string, detail?: string) {
    super.record(message, detail);
  }
}

class EditUserAop extends SuperAop {
  constructor(props: ISuperAopPros) {
    super(props);
  }
  async record() {
    const editKeys = Object.keys(this.requestData).filter(key => this.requestData[key] !== this.resultData[key]);
    const detail = editKeys.map(key => key + '由' + this.resultData[key] + '改为' + this.requestData[key]).join(',');
    super.record(this.message, detail);
  }
}

class LoginAop extends SuperAop {
  constructor(props: ISuperAopPros) {
    super(props);
  }
  async record() {
    super.record(this.message);
  }
}

class DeleteUserAop extends SuperAop {
  constructor(props: ISuperAopPros) {
    super(props);
  }
  async record() {
    super.record(this.message);
  }
}

class RestUserPwd extends SuperAop {
  constructor(props: ISuperAopPros) {
    super(props);
  }
  async record() {
    super.record(this.message);
  }
}

const aopManager = new AopManager();
aopManager.addAop(EOperationType.EDITUSER, EditUserAop);
aopManager.addAop(EOperationType.LOGIN, LoginAop);
aopManager.addAop(EOperationType.DELUSER, DeleteUserAop);
aopManager.addAop(EOperationType.RESETPWD, RestUserPwd);
console.log('aopManager', aopManager);

export default function recordAop(recordCode: string) {
  return (_target, _name: string, descriptor: PropertyDescriptor) => {
    const oldValue = descriptor.value;
    descriptor.value = async function() {
      const result = (await oldValue.apply(this,arguments)) as IAopController;
      console.log(result,'result')
      if (result.success) {
        // @ts-ignore
        const ctx = this.ctx as Context;
        aopManager.record({ ctx, code: recordCode, ...result });
      }
      return result;
    };
    return descriptor;
  };
}
